import { LocalLanguage, MDMSerializerOptions, Mutable } from "../../common/types";
import { Diagnostic } from "../diagnostic";
import { MDMDocument as MetadataDocument } from "../analysis/types";

export enum ObjectFlags {
    variable = 0,
    array = 1,
    grid = 2,
    class = 3,
    element = 4,
    elements = 5,
    label = 6,
    field = 7,
    helperFields = 8,
    fields = 9,
    types = 10, // 0x0000000A
    properties = 11, // 0x0000000B
    routing = 12, // 0x0000000C
    contexts = 13, // 0x0000000D
    languages = 14, // 0x0000000E
    levelObject = 15, // 0x0000000F
    variableInstance = 16, // 0x00000010
    routingItem = 17, // 0x00000011
    compound = 18, // 0x00000012
    elementInstance = 19, // 0x00000013
    elementInstances = 20, // 0x00000014
    language = 21, // 0x00000015
    routingItems = 22, // 0x00000016
    ranges = 23, // 0x00000017
    categories = 24, // 0x00000018
    categoryMap = 25, // 0x00000019
    dataSources = 26, // 0x0000001A
    document = 27, // 0x0000001B
    version = 29, // 0x0000001D
    versions = 30, // 0x0000001E
    variables = 31, // 0x0000001F
    dataSource = 32, // 0x00000020
    aliasMap = 33, // 0x00000021
    indexElement = 34, // 0x00000022
    indicesElements = 35, // 0x00000023
    pages = 36, // 0x00000024
    parameters = 37, // 0x00000025
    page = 38, // 0x00000026
    items = 39, // 0x00000027
    context = 40, // 0x00000028
    contextAlternatives = 41, // 0x00000029
    elementList = 42, // 0x0000002A
    goto = 43, // 0x0000002B
    template = 44, // 0x0000002C
    templates = 45, // 0x0000002D
    style = 46, // 0x0000002E
    note = 47, // 0x0000002F
    notes = 48, // 0x00000030
    ifBlock = 49, // 0x00000031
    conditionalRouting = 50, // 0x00000032
    dBElements = 51, // 0x00000033
    dBQuestionDataProvider = 52, // 0x00000034
    unknown = 65535, // 0x0000FFFF
}

export enum Alignments {
    default,
    left,
    center,
    right,
    justify,
}

export enum AudioControlPositions {
    left,
    right,
    top,
    bottom,
}

export enum BorderStyles {
    none,
    solid,
    double,
    groove,
    ridge,
    inset,
    outset,
}

export enum CategoryFilterFlags {
    all,
    category,
    expression,
    other,
    element,
}

export enum CategoryFlags {
    none          = 0,
    user          = 1 << 0,
    dontknow      = 1 << 1,
    refuse        = 1 << 2,
    noanswer      = 1 << 3,
    other         = 1 << 4,
    multiplier    = 1 << 5,
    fixedPosition = 1 << 6,
    noFilter      = 1 << 7,
    inline        = 1 << 8,
    exclusive     = 1 << 12,
    missing       = dontknow | refuse | noanswer
}

export enum ChangeTypes {
  add = 1,
  name = 2,
  type = 3,
  remove = 4,
  document = 5,
  parent = 6,
  loadUnloaded = 7,
  properties = 8,
}

export enum ContextUsageFlags {
    routings = 1,
    labels = 2,
    properties = 4,
}

export enum ControlTypes {
    static = 0,
    edit = 1,
    singleLineEdit = 2,
    multiLineEdit = 3,
    dropList = 4,
    comboList = 5,
    radioButton = 6,
    checkButton = 7,
    listBox = 8,
    listControl = 9,
    button = 10,
    date = 11,
    time = 12,
    dateTime = 13,
    password = 14,
}

export enum CursorTypes {
    auto,
    crossHair,
    default,
    pointer,
    move,
    eResize,
    nEResize,
    nResize,
    nWResize,
    wResize,
    sWResize,
    sResize,
    sEResize,
    text,
    wait,
    help,
}

export enum DataTypes {
    none = 0,
    long = 1,
    text = 2,
    categorical = 3,
    date = 5,
    double = 6,
    boolean = 7,
}

export enum DBQuestionsTypes {
    unknown,
    singleResponse,
    multipleResponse,
    array,
}

export enum ElementAlignments {
    default,
    right,
    newLine,
}

export enum ElementTypes {
    category = 0,
    analysisSubheading = 1,
    analysisBase = 2,
    analysisSubtotal = 3,
    analysisSummaryData = 4,
    analysisDerived = 5,
    analysisTotal = 6,
    analysisMean = 7,
    analysisStdDev = 8,
    analysisStdErr = 9,
    analysisSampleVariance = 10,
    analysisMinimum = 11,
    analysisMaximum = 12,
    categoryList = 13,
    analysisCategory = 14,
    arrayLevel = 4096,
    arrayIndex = 4097,
    arrayCategory = 4098,
}

export enum FilterTypes {
    include,
    exclude,
}

export enum ImagePositions {
    left,
    right,
    top,
    bottom,
    imageOnly,
    none,
}

export enum InfoTypes {
    values = 1,
    missing = 2,
    flags = 3,
    other = 4,
}

export enum InterviewModesFlags {
    empty            = 0,
    phone            = 1 << 0,
    web              = 1 << 1,
    local            = 1 << 2,
    paperScanning    = 1 << 3,
    paperManualEntry = 1 << 4
}

export enum IteratorTypes {
    number = 1,
    categorical = 2,
    numericRanges = 3,
}

export enum OpenFlags {
    read = 1,
    readWrite = 2,
    noSave = 3,
}

export enum DisplayOrders {
    normal,
    randomize,
    rotate,
    reverse,
    ascending,
    descending,
}

export enum DisplayOrientations {
    row,
    col,
}

export enum Orientations {
    default,
    row,
    column,
}

export enum RecordModes {
    none,
    manual,
    auto,
    prohibited,
}

export enum RoutingItemsFlags {
    array = 1,
    expandedArray = 2,
    expandedClass = 4,
    expandedGrid = 8,
    expandedCompound = 16,
    empty = 32,
    expandedPage = 64,
    excludeHidden = 128,
}

export enum RoutingScriptOptions {
    default,
    exitWithItem,
}

export enum SourceTypes {
    dataField = 1,
    expression = 2,
    expressions = 4,
    noCaseData = 8,
    none = 4096
}

export enum VariableUsageFlags {
    variable     = 0,
    grid         = 1 << 0,
    compound     = 1 << 1,
    class        = 1 << 2,
    array        = 1 << 3,
    helperField  = 1 << 4,
    sourceFile   = 1 << 8  | helperField,
    coding       = 1 << 9  | helperField,
    otherSpecify = 1 << 10 | helperField,
    multiplier   = 1 << 11 | helperField,
    filter       = 1 << 12,
    weight       = 1 << 13,
}

export enum VerticalAlignments {
    default,
    baseline,
    middle,
    sub,
    super,
    textTop,
    textBottom,
    top,
    bottom,
}

export enum FontStyleEffects {
    isUnderline     = 1 << 0,
    isItalic        = 1 << 1,
    isBold          = 1 << 2,
    isStrikethrough = 1 << 3,
    isOverline      = 1 << 4,
    isBlink         = 1 << 5,
    isSuperscript   = 1 << 6,
    isSubscript     = 1 << 7,
}

export interface IMDMBase {
    readonly document: BaseMDMDocument;
}

export interface ContextMapItem<T> {
    readonly context: string;
    readonly value: T;
}

export interface ContextMap<T> extends MDMListOf<ContextMapItem<T>> {
    getItem(context: string): T | undefined;
    getItem(predicate: (element: T) => boolean): T | undefined;
    setItem(context: string, element: T): void;
    getAllItems(predicate: (element: T) => boolean): ContextMapItem<T>[];
    removeItem(context: string): void;
    removeItem(predicate: (element: T) => boolean): void;
    countItem(context: string, callback: (item: T) => number): number;
}

export type MutableContextMap<T> = MutableMDMListOf<ContextMapItem<T>>;

export interface ControlStyle {
    readonly context: string;
    readonly type?: ControlTypes;
    readonly readonly?: boolean;
    readonly accelerator?: string;
}

export interface FontStyle {
    readonly context: string;
    readonly family?: string;
    readonly size?: number;
    readonly effects?: number;
}

//export interface IAudioStyle extends IMDMBase {
//    name?: string;
//    playControlPosition?: AudioControlPositions;
//    recordControlPosition?: AudioControlPositions;
//    record?: RecordModes;
//}

export interface CellStyle {
    readonly context: string;
    readonly width?: string;
    readonly height?: string;
    readonly borderColor?: string;
    readonly borderTopColor?: string;
    readonly borderRightColor?: string;
    readonly borderBottomColor?: string;
    readonly borderLeftColor?: string;
    readonly borderStyle?: BorderStyles;
    readonly borderTopStyle?: BorderStyles;
    readonly borderRightStyle?: BorderStyles;
    readonly borderBottomStyle?: BorderStyles;
    readonly borderLeftStyle?: BorderStyles;
    readonly borderWidth?: number;
    readonly borderTopWidth?: number;
    readonly borderRightWidth?: number;
    readonly borderBottomWidth?: number;
    readonly borderLeftWidth?: number;
    readonly padding?: number;
    readonly paddingTop?: number;
    readonly paddingRight?: number;
    readonly paddingBottom?: number;
    readonly paddingLeft?: number;
    readonly wrap?: boolean;
    readonly colSpan?: number;
    readonly rowSpan?: number;
    readonly bgColor?: string;
    readonly repeatHeader?: number;
    readonly repeatSideHeader?: number;
}

export interface Style {
    readonly objectFlag: ObjectFlags.style;
    readonly context: string;
    readonly parent?: Style;
    readonly color?: string;
    readonly bgColor?: string;
    readonly hidden?: boolean;
    readonly align?: Alignments;
    readonly verticalAlign?: VerticalAlignments;
    readonly elementAlign?: ElementAlignments;
    readonly indent?: number;
    readonly zIndex?: number;
    readonly cursor?: CursorTypes;
    readonly image?: string;
    readonly imagePosition?: ImagePositions;
    readonly orientation?: Orientations;
    readonly columns?: number;
    readonly rows?: number;
    readonly width?: string;
    readonly height?: string;
    readonly cell?: CellStyle;
    readonly control?: ControlStyle;
    readonly font?: FontStyle;
    //audio?: IAudioStyle;
}

export interface BaseMDMDocument extends IMDMLabeledObject {
    readonly dom: XMLDocument;
    readonly properties: Properties;
    readonly templates: Templates;
    readonly languages: Languages;              // 由 loader 初始化
    readonly contexts: Contexts;                // 由 loader 初始化
    readonly labelTypes: Contexts;              // 由 loader 初始化
    readonly routingContexts: Contexts;         // 由 loader 初始化
    readonly scriptTypes: Contexts;             // 由 loader 初始化
    readonly versions: Versions;
    readonly definitions: ReadonlyMap<string, IMDMLabeledObject>;
    readonly fields: Types<IMDMLabeledObject>;
    readonly types: Types<MDMElements>;
    readonly systemVariables: Types<Class>;
    readonly systemRouting: Routings;
    readonly routings: Routings;
    readonly categoryMap: CategoryMap;
    readonly aliasMap: AliasMap;
    readonly dataSources: DataSources;
    readonly createdByVersion: string;
    readonly lastUpdatedByVersion: string;
    readonly mdmVersion: string;
    readonly saveLogs: SaveLogs;
    readonly pages: Pages;
    readonly lcl: LCLMap;
    readonly currentVersion: string;
    readonly url: string;
    readonly openMode: OpenFlags;
    readonly projectDirectory: string;
    readonly atoms: readonly string[];
    readonly mapping: readonly VariableInstance[];
    setCurrentContext(context: string): void;
    setCurrentLanguage(language: string): void;
    setCurrentLabelType(labelType: string): void;
    setCurrentRoutingContext(routingContext: string): void;
    getLCLSet(context?: string): Set<string> | undefined;
    updateLCLSet(language: string, context: string): void;
    updateCategoryMap(name: string): boolean;
    getDefinition<T>(uuid: string | undefined, predicate: (o: any) => o is T): T | undefined;
    getDefinitionByName<T extends IMDMLabeledObject>(name: string, predicate: (o: any) => o is T): T[];
    updateDefinition<T extends IMDMLabeledObject>(obj: T): void;
    updateAtom(name: string): boolean;
}

export interface MDMSerializerExportFiles {
    readonly scriptFile: string;
    readonly routingFile: string;
}

export interface MDMDocument extends BaseMDMDocument {
    serializeToScript(options?: MDMSerializerOptions): string;
    serializeToXml(options?: MDMSerializerOptions): Document;
    updateFromScript(script: string): void;
    save(destination?: string): void;
    open(path: string): readonly Diagnostic[];
    close(): void;
    /**
     * 输出序列化后的.mdd文档文件
     * @param destination 目标文件夹路径
     * @param options 指定的元数据配置对象
     */
    exportSerializedFile(destination: MDMSerializerExportFiles, options?: MDMSerializerOptions): void;
    exportSerializedFile(options?: MDMSerializerOptions): MDMSerializerExportFiles;
}

//
// MDM文档使用LCL定义多语言和上下文的描述、属性等配置，在文档整体语言配置基础上，应当区分不同的上下文类型。
// 例如：
//      CONTEXT   LANGUAGE   LABEL
//      ANALYSIS  zh-CN      "ANALYSIS CN"
//                en-GB      "ANALYSIS GB"
//                en-US      "ANALYSIS US"
//                ...        ...
//      WEBAPP    zh-CN      "WEBAPP CN"
//                en-GB      "WEBAPP GB"
//                ...
//
// 当文档语言配置为zh-CN且显示多种上下文时，描述应当显示如下:
//   ANALYSIS: "ANALYSIS CN"
//   WEBAPP: "WEBAPP CN"
//
// 注意：LCL的命名不重要也不会保存命名，最终会追加到指定的上下文和语言类型集合中，并且序列化时也不会输出原始命名。
//

// Map: Key - 小写语言  Value - 小写上下文类型集合
export type LCLMap = ReadonlyMap<string, Set<string>>;

export interface IMDMObject extends IMDMBase {
    readonly objectFlag: ObjectFlags;
    readonly parent: IMDMObject;
    readonly reference?: IMDMObject;
    readonly uuid: string;
    readonly properties: Properties;
    readonly name: string;
}

export interface IMDMLabeledObject extends IMDMObject {
    readonly reference?: IMDMLabeledObject;
    readonly labels: Labels;
    readonly styles: Styles;
    readonly labelStyles: Styles;
    readonly templates: Templates;
    readonly notes: Notes;
}

export interface Language extends IMDMBase {
    readonly objectFlag: ObjectFlags.language;
    readonly document: BaseMDMDocument;
    readonly shortName: string;
    readonly longName: string;
    readonly id: string;
    readonly properties: Properties;
    readonly alternatives?: MDMListOf<string>;
}

export interface Languages extends IMDMBase, MDMListOf<Language> {
    readonly objectFlag: ObjectFlags.languages;
    readonly current: string;
    readonly base: string;
    readonly properties?: Properties;
}

export interface ContextAlternatives extends IMDMBase, MDMListOf<string> {
    readonly objectFlag: ObjectFlags.contextAlternatives;
}

export interface Context extends IMDMBase {
    readonly objectFlag: ObjectFlags.context;
    readonly properties?: Properties;
    readonly alternatives?: ContextAlternatives;
    readonly name: string;
    readonly usage: ContextUsageFlags;
}

export interface Contexts extends IMDMBase, MDMListOf<Context> {
    readonly objectFlag: ObjectFlags.contexts;
    readonly properties?: Properties;
    readonly base: string;
    readonly current: string;
}

export interface VersionObject<T> {
    readonly name: string;
    readonly data: T;
}

export interface Version extends IMDMObject {
    readonly objectFlag: ObjectFlags.version;
    readonly subVersions?: Versions;
    readonly fullName: string;
    readonly isLocked: boolean;
    readonly dateCreated: string;
    readonly dateLocked?: string;
    readonly createdBy: string;
    readonly description: string;
    readonly label: string;
    readonly mdmVersion?: string;
    readonly major?: string;
    readonly minor?: string;
}

export interface Versions extends IMDMObject, MDMListOf<Version> {
    readonly objectFlag: ObjectFlags.versions;
    readonly latest?: Version;
}

export type PropertyValue = string | number | boolean | Properties;

export interface Property {
    readonly name: string;
    readonly context: string;
    readonly valueType?: number;
    readonly value?: PropertyValue;
    readonly dataSource?: string;
    readonly versioned: boolean;
}

export interface Properties extends IMDMBase, ContextMap<Property[]> {
    readonly objectFlag: ObjectFlags.properties;
    readonly id?: string;
    readonly name?: string;
    addNew(contextAddTo: string, property: Property): Property;
    addNew(contextAddTo: string, name: string, context: string, value?: PropertyValue, dataSource?: string): Property;
}

export interface Template extends IMDMBase {
    readonly objectFlag: ObjectFlags.template;
    readonly name: string;
    readonly context: string;
    readonly value: string;
}

export interface Templates extends IMDMBase, ContextMap<Template[]> {
    readonly objectFlag: ObjectFlags.templates;
    addNew(document: BaseMDMDocument, contextAddTo: string, name: string, context: string, value: string): Template;
}

export type Styles = ContextMap<Style>;

export interface LabelObject {
    readonly context: string;
    readonly language: string;
    readonly text: string;
    readonly cdata?: boolean;
}

export interface Label extends MDMListOf<LabelObject>, IMDMBase {
    readonly context: string; // 此处 context 表示 BaseMDMDocument 中的 labelType
    getText(context: string, language: string): string | undefined;
    setText(context: string, language: string, text: string): void;
    removeText(context: string, language: string): void;
}

// Labels 的 上下文类型 指的是文档配置的labelType属性类型
export interface Labels extends ContextMap<Label>, IMDMBase {
    /**
     * 根据指定的文本类型、上下文类型和语言，获取指定配置的文本，如果不存在，返回undefined。
     * @param targetContext 此处的上下文类型指的是文档的LabelType，不是Context
     * @param context 文本的上下文类型，此处指的是Context
     * @param language 文本的语言类型
     */
    getText(targetContext: string | undefined, context: string, language: string): string | undefined;
    /**
     * 设定指定的文本类型、上下文类型和语言的文本描述内容。
     * @param targetContext 此处的上下文类型指的是文档的LabelType，不是Context
     * @param context 文本的上下文类型，此处指的是Context
     * @param language 文本的语言类型
     * @param text 需要设置的描述文本
     */
    setText(targetContext: string | undefined, context: string, language: string, text: string): void;
    /**
     * 移除指定文本类型的Label对象。
     * @param labelType 文本类型
     */
    clearText(labelType: string): void;
}

export interface User {
    readonly name: string;
    readonly fileVersion: string;
    readonly comment: string;
}

export interface SaveLog {
    readonly date: string;
    readonly versionSet: string;
    readonly mdmFileVersion: string;
    readonly user: User;
    readonly saveCount: number;
    readonly userName: string;
}

export type SaveLogs = readonly SaveLog[];

export interface Note {
    readonly objectFlag: ObjectFlags.note;
    readonly value: string;
    readonly name: string;
    readonly context: string;
}

export interface Notes extends MDMListOf<Note> {
    readonly objectFlag: ObjectFlags.notes;
}

export interface Expression {
    readonly name: string;
    readonly value: string;
}

export type Expressions = readonly Expression[];

export type MDMElementTypeName =
| "Category"
| "AnalysisSubheading"
| "AnalysisBase"
| "AnalysisSubtotal"
| "AnalysisSummaryData"
| "AnalysisDerived"
| "AnalysisTotal"
| "AnalysisMean"
| "AnalysisStdDev"
| "AnalysisStdErr"
| "AnalysisSampleVariance"
| "AnalysisMinimum"
| "AnalysisMaximum"
| "CategoryList"
| "AnalysisCategory"
| "ArrayLevel"
| "ArrayIndex"
| "ArrayCategory"
;

export interface MDMElements extends IMDMLabeledObject, MDMListOf<IMDMLabeledObject> {
    readonly objectFlag: ObjectFlags.elements;
    readonly fullName: string;
    readonly dataSourceProperties?: Properties;
    readonly isDerived: boolean;
    readonly expanded?: MDMElement[];
    readonly ownerField?: Field;
    readonly isDBElements: boolean;
    readonly dbFilter?: string;
    readonly value?: number;
    readonly reference?: MDMElements;
    readonly versioned: boolean;
    readonly namespace: boolean;
    readonly displayOrder: DisplayOrders;
    readonly fixed: boolean;
    readonly referenceName?: string;
    readonly inline: boolean;
}

export interface MDMElement extends IMDMLabeledObject {
    readonly reference?: MDMElement;
    readonly type: ElementTypes;
    readonly flag: CategoryFlags;
    readonly factor?: number | string | boolean;
    readonly otherVariable?: Variable;
    readonly multiplierVariable?: Variable;
    readonly versioned: boolean;
    readonly parent: IMDMObject;
    readonly fullName: string;
    readonly dataSourceProperties?: Properties;
    readonly ownerField?: Field;
    readonly value?: number;
    readonly nv?: number;
    readonly fixed: boolean;
    readonly expression?: string;
    readonly autoElements?: boolean;
    readonly script?: any;
    readonly referenceName?: string;
    readonly isDerived: boolean;
    readonly typeName: MDMElementTypeName;
    readonly hasNoData: boolean;
    readonly canFilter: boolean;
    readonly keyCode?: string;
    readonly nativeValue?: any;
}

export interface MDMElementInstance extends IMDMObject {
    readonly objectFlag: ObjectFlags.elementInstance;
    readonly fullName?: string;
    readonly element?: MDMElement;
}

export interface MDMElementInstances extends MDMListOf<IMDMObject> {
    readonly objectFlag: ObjectFlags.elementInstances;
}

export interface MDMListOf<T> extends ReadonlyArray<T> {
    readonly deleted: readonly T[];
    update(item: T): void;
    update(items: T[]): void;
    updateDeleted(item: T): void;
    updateDeleted(items: T[]): void;
}

export interface MutableMDMListOf<T> extends Array<T> {
    deleted: T[];
}

export interface Categories extends MDMListOf<MDMElement> {
    readonly fullName: string;
    readonly value?: number;
    readonly categoryFilter: CategoryFilterFlags;
}

export interface Types<T extends IMDMObject> extends ReadonlyMap<string, T> {
    readonly objectFlag: ObjectFlags;
    readonly parent: IMDMObject;
    readonly uuid: string;
    readonly expanded?: Types<T>;
    readonly deleted: ReadonlyMap<string, T>;
    readonly unversioned?: Types<T>;
    update(item: T): void;
    updateDeleted(item: T): void;
}

export interface Field extends IMDMLabeledObject {
    readonly elements: MDMElements;
    //readonly categories: Categories;
    readonly hasCaseData: boolean;
    readonly helperFields: Types<Variable>;
    readonly reference?: Field;
    readonly dataType: DataTypes;
    readonly minValue?: string | number;
    readonly maxValue?: string | number;
    readonly axisExpression?: string;
    readonly ranges?: Ranges;
    readonly rangeExpression?: string;
    readonly expression?: string;
    readonly autoElements?: boolean;  // true - DeriveElements
    readonly sourceType: SourceTypes;
    readonly codes?: Variable;
    readonly defaultAnswer?: string | number | boolean;
    readonly initialAnswer?: string | number | boolean;
    readonly scale?: number;
    readonly precisions?: number;
    readonly validation?: string;
    readonly versioned: boolean;
    readonly upperBound?: string;
    readonly lowerBound?: string;
}

export interface Variable extends Field {
    readonly baseName: string;
    readonly usageType: VariableUsageFlags;
    readonly dbType?: DBQuestionsTypes;
    readonly dbDataType?: DataTypes;
    readonly dbQuestionProperties?: DBQuestionProperties;
    readonly infoType?: InfoTypes;
}

export interface VariableInstance extends IMDMObject {
    readonly objectFlag: ObjectFlags.variableInstance;
    readonly variable: Variable;
    readonly fullName: string;
    readonly sourceType?: SourceTypes;
    readonly elements?: MDMElementInstances;
}

export interface Variables extends ReadonlyMap<string, Variable> {
    readonly objectFlag: ObjectFlags.variables;
    readonly children?: Variables;
}

export interface Class extends IMDMLabeledObject {
    readonly fields: Types<IMDMLabeledObject>;
    readonly types: Types<MDMElements>;
    readonly helperFields: Types<IMDMLabeledObject>;
    readonly fullName: string;
    readonly dataSourceProperties: Properties;
    readonly relativeName: string;
    readonly items: Types<IMDMObject>;
    readonly pages?: Pages;
    readonly routings?: Routings;
    readonly indices?: Variable;
    readonly versioned: boolean;
    readonly namespace: boolean;
    readonly script: any;
}

export interface Range {
    readonly lowerBound: number | string;
    readonly upperBound: number | string;
}

export type Ranges = readonly Range[];

export interface MDMField extends Field {
    readonly elements: MDMElements;
    // readonly categories: Categories;
    readonly helperFields: Types<Variable>;
    readonly fullName: string;
    readonly dataSourceProperties?: Properties;
    readonly variables: Variables;
    readonly expressions?: Expressions;
    readonly fields: Types<IMDMLabeledObject>;
    readonly isDerived: boolean;
    // readonly otherCategories?: Categories;
    readonly dbQuestionProperties?: DBQuestionProperties;
    readonly dbQuestionType?: DBQuestionsTypes;
    readonly dbDataType?: DataTypes;
    readonly dataType: DataTypes;
    readonly minValue: any;
    readonly maxValue: any;
    readonly effectiveMinValue: any;
    readonly effectiveMaxValue: any;
    readonly usageType: VariableUsageFlags;
    readonly iteratorType?: IteratorTypes;
    readonly lowerBound: any;
    readonly upperBound: any;
    readonly orientation?: DisplayOrientations;
    readonly isGrid: boolean;
    readonly expand: boolean;
    readonly class: Class;
}

export interface Compound extends Field {
    readonly objectFlag: ObjectFlags.compound;
    readonly fields: Types<IMDMLabeledObject>;
    readonly types: Types<MDMElements>;
    readonly helperFields: Types<Variable>;
    readonly routing?: Routing;
    readonly versioned: boolean;
    readonly namespace: boolean;
    readonly fullName: string;
    readonly orientation?: DisplayOrientations;
    readonly otherCategories?: Categories;
    readonly pages: Pages;
    readonly isValid: boolean;
}

export interface Page extends IMDMLabeledObject, MDMListOf<Variable> {
    readonly fullName: string;
    readonly relativeName: string;
    readonly script: any;
}

export interface Pages extends Types<Page> {
    readonly objectFlag: ObjectFlags.pages;
    readonly name: string;
    readonly namespace: boolean;
}

export interface CategoryMap extends IMDMBase {
    readonly nameToValueMap: ReadonlyMap<string, number>;
    readonly valueToNameMap: ReadonlyMap<number, string>;
}

export interface NativeValue {
    readonly fullName: string;
    readonly value: string;
}

export type NativeValues = readonly NativeValue[];

export interface SubAlias {
    readonly index: number;
    readonly name: string;
}

export interface Alias extends MDMListOf<SubAlias> {
    readonly properties: Properties;
    readonly nativeValues: NativeValues;
}

export interface AliasMap extends MDMListOf<Alias> {
    readonly objectFlag: ObjectFlags.aliasMap;
}

export interface DataSource {
    readonly uuid: string;
    readonly name: string;
    readonly dbLocation: string;
    readonly cdscName: string;
    readonly project: string;
    readonly aliasMap?: AliasMap;
}

export interface DataSources extends ReadonlyArray<DataSource> {
    readonly default?: DataSource;
}

export interface Script {
    readonly script: string;
    readonly name: string;
    readonly isDefault: boolean;
}

export interface ScriptType extends MDMListOf<Script> {
    readonly type: string;
    readonly context: string;
    readonly interviewMode: InterviewModesFlags;
    readonly useKeyCode: boolean;
}

export type Scripts = MDMListOf<ScriptType>;

export interface RoutingItem {
    readonly name: string;
    readonly item: string;
}

export interface Routing extends MDMListOf<RoutingItem> {
    readonly objectFlag: ObjectFlags.routing;
    readonly context: string;
    readonly interviewMode: InterviewModesFlags;
    readonly useKeyCode: boolean;
}

export interface Routings extends MDMListOf<Routing> {
    readonly name?: string;
    readonly scripts: Scripts;
    readonly properties: Properties;
}

export interface DBColumnMapItems {
    readonly id: string;
    readonly label: string;
    readonly keyCode?: string;
    readonly file?: string;
    readonly analysisValue?: string;
    readonly fixed?: string;
    readonly exclusive?: string;
}

export interface DBQuestionProperties {
    readonly columns?: DBColumnMapItems;
    readonly connectionString: string;
    readonly table: string;
    readonly minAnswers?: number;
    readonly maxAnswers?: number;
    readonly sqlFilter?: string;
    readonly cacheTimeout?: number;
    readonly iteratorIDType?: "text" | "double" | "long" | "date";
    readonly cacheConnection?: boolean;
}

// object allocator

export interface ObjectAllocator {
    getMDMObjectConstructor(): new (document: BaseMDMDocument, parent: IMDMObject, flag: ObjectFlags, name: string, uuid?: string) => IMDMObject;
    getMDMListConstructor(): new <T>(items?: readonly T[], deleted?: readonly T[]) => MDMListOf<T>;
    getContextMapConstructor(): new <T>(items?: readonly ContextMapItem<T>[], deleted?: readonly ContextMapItem<T>[]) => ContextMap<T>;
    getPropertiesConstructor(): new (document: BaseMDMDocument, id?: string, name?: string, items?: readonly ContextMapItem<Property[]>[]) => Properties;
    getTemplatiesConstructor(): new (document: BaseMDMDocument, items?: readonly ContextMapItem<Template[]>[]) => Templates;
    getLabelConstructor(): new (document: BaseMDMDocument, context: string, items?: readonly LabelObject[]) => Label;
    getLabelsConstructor(): new (document: BaseMDMDocument, entries?: readonly ContextMapItem<Label>[]) => Labels;
    getTypesConstructor(): new <T extends IMDMObject>(parent: IMDMObject, uuid?: string, entries?: readonly [string, T][]) => Types<T>;
    getLabeledObjectConstructor(): new (document: BaseMDMDocument, parent: IMDMObject, flag: ObjectFlags, name: string, uuid?: string, reference?: IMDMLabeledObject) => IMDMLabeledObject;
    getBaseDocumentConstructor(): new () => BaseMDMDocument;
}

export interface MDMFactory {
    createObject(document: BaseMDMDocument, parent: IMDMObject, flag: ObjectFlags, name: string, uuid?: string): Mutable<IMDMObject>;
    createMDMList<T>(items?: readonly T[], deleted?: readonly T[]): MDMListOf<T>;
    createContextMap<T>(entries?: readonly ContextMapItem<T>[], deleted?: readonly ContextMapItem<T>[]): ContextMap<T>;
    createDataSource(name: string, uuid: string | undefined, dbLocation: string, cdscName: string, project: string): DataSource;
    createDataSources(defaultObj?: DataSource, items?: readonly DataSource[]): DataSources;
    createVersion(document: BaseMDMDocument, parent: IMDMObject, name: string, dateCreated: string, createdBy: string, description: string): Version;
    createVersions(document: BaseMDMDocument, parent: IMDMObject, latest?: Version): Versions;
    createControlStyle(context: string, type?: ControlTypes, readonly?: boolean, accelerator?: string): Mutable<ControlStyle>;
    createFontStyle(context: string, family?: string, size?: number, effects?: number): Mutable<FontStyle>;
    createCellStyle(context: string): Mutable<CellStyle>;
    createStyle(context: string): Mutable<Style>;
    createLabelObject(context: string, language: string, text: string, cdata?: boolean): LabelObject;
    createProperty(name: string, context: string, value?: PropertyValue | null, dataSource?: string): Property;
    createProperties(document: BaseMDMDocument, id?: string, name?: string, items?: readonly ContextMapItem<Property[]>[]): Properties;
    createTemplate(document: BaseMDMDocument, name: string, context: string, value: string): Template;
    createTemplates(document: BaseMDMDocument, items?: readonly ContextMapItem<Template[]>[]): Templates;
    createLabel(document: BaseMDMDocument, context: string, labelObjects?: readonly LabelObject[] | MDMListOf<LabelObject>): Label;
    createLabels(document: BaseMDMDocument, entries?: readonly ContextMapItem<Label>[]): Labels;
    createContextAlternatives(document: BaseMDMDocument, alternatives?: readonly string[]): ContextAlternatives;
    createContext(document: BaseMDMDocument, name: string, usage?: ContextUsageFlags): Context;
    createContexts(document: BaseMDMDocument, base: string, current: string): Contexts;
    createLanguage(document: BaseMDMDocument, language: LocalLanguage, id: string): Language;
    createLanguages(document: BaseMDMDocument, base: string, current: string): Languages;
    createLabeledObject(document: BaseMDMDocument, parent: IMDMObject, flag: ObjectFlags, name: string, id?: string, reference?: IMDMLabeledObject): Mutable<IMDMLabeledObject>;
    createCategoryMap(document: BaseMDMDocument, entries?: readonly [string, number][]): CategoryMap;
    createRange(lowerBoundary: number | string, upperBoundary: number | string): Range;
    createTypes<T extends IMDMObject>(parent: any, uuid?: string, entries?: readonly [string, T][]): Types<T>;
    createUser(name: string, fileVersion: string, comment: string): User;
    createSaveLog(date: string, versionSet: string, mdmFileVersion: string, userName: string, saveCount: number): SaveLog;
    createNote(value: string, name: string, context: string): Note;
    createNotes(items?: readonly Note[]): Notes;
    createCategories(fullName?: string, value?: number, filterFlag?: CategoryFilterFlags, items?: readonly MDMElement[]): Categories;
    createMDMElement(document: BaseMDMDocument, parent: IMDMObject, name: string, type: ElementTypes, ownerField: Field | undefined, uuid?: string, categoryFlag?: CategoryFlags): MDMElement;
    createMDMElements(document: BaseMDMDocument, parent: IMDMObject, ownerField?: Field, name?: string, uuid?: string, reference?: MDMElements, items?: readonly IMDMLabeledObject[]): MDMElements;
    createField(document: BaseMDMDocument, parent: IMDMObject, name: string, dataType: DataTypes, uuid?: string): Field;
    createVariable(document: BaseMDMDocument, parent: IMDMObject, name: string, dataType: DataTypes, uuid?: string): Variable;
    createVariables(items?: readonly Variable[], children?: readonly Variable[]): Variables;
    createMDMElementInstace(document: BaseMDMDocument, parent: IMDMObject, fullName?: string): MDMElementInstance;
    createMDMElementInstances(items?: readonly MDMElementInstance[], deleted?: readonly MDMElementInstance[]): MDMElementInstances;
    createVariableInstance(parent: IMDMObject, variable: Variable, fullName?: string, uuid?: string): VariableInstance;
    createClass(document: BaseMDMDocument, parent: IMDMObject, name?: string, id?: string): Class;
    createMDMField(document: BaseMDMDocument, parent: IMDMObject, name: string, dataType: DataTypes, uuid?: string): MDMField;
    createCompound(document: BaseMDMDocument, parent: IMDMObject, name: string, uuid?: string): Compound;
    createPage(document: BaseMDMDocument, parent: IMDMObject, name: string, uuid?: string, fields?: readonly Field[]): Page;
    createPages(parent: IMDMObject, name: string, uuid?: string, items?: readonly Page[], deleted?: readonly Page[]): Pages;
    createAliasMap(alias?: readonly Alias[]): AliasMap;
    createAlias(document: BaseMDMDocument, items?: readonly SubAlias[]): Alias;
    createScript(name: string, script: string, isDefault: boolean): Script;
    createScriptType(type: string, context: string, interviewMode: InterviewModesFlags, useKeyCode?: boolean, items?: readonly Script[]): ScriptType;
    createRouting(context: string, interviewMode?: InterviewModesFlags, useKeyCode?: boolean, items?: readonly RoutingItem[]): Routing;
    createRoutings(document: BaseMDMDocument, name?: string, scripts?: Scripts, properties?: Properties, items?: readonly Routing[]): Routings;
    createBaseDocument(): BaseMDMDocument;
}

export interface MDMLoaderFromScriptState {
    readonly currentContext: string;
    readonly currentLabelType: string;
    readonly currentLanguage: LocalLanguage;
}

export interface MDMLoader {
    readonly factory: MDMFactory;
    readonly xmlErrors: readonly Diagnostic[];
    readonly scriptErrors: readonly Diagnostic[];
    clear(): void;
    loadTo(document: BaseMDMDocument, url: string, content?: string): void;
    loadFromFile(url: string): BaseMDMDocument;
    loadFromString(url: string, content: string): BaseMDMDocument;
    loadFromScript(document: MetadataDocument): BaseMDMDocument;
}

export interface Serializer {
    readonly errors: readonly Diagnostic[];
    serializeToXml(document: BaseMDMDocument, options?: MDMSerializerOptions): Document;
    serializeToString(document: BaseMDMDocument, options?: MDMSerializerOptions): string;
}

